#include "Framework.hpp"
#include "Shader.hpp"
#include "RenderObject.hpp"
#include "Camera.hpp"

/*Window variables*/
int screenWidth=1280;
int screenHeight=960;
sf::WindowSettings settings(24, 8, 2);
sf::RenderWindow window(sf::VideoMode(screenWidth, screenHeight), "CS248 Rules!", sf::Style::Close, settings);
sf::Clock clk;
bool windowInFocus = true;

/*object*/
RenderObject* test_obj;

/*shader*/
Shader* noLightShader;

Camera camera;

//debug
float fpsAcc=0.0; int fps = 0;
sf::String fpsstr;
GLenum wireMode = GL_FILL;


void initOpenGL();
void loadAssets();
void finalInit();
void handleInput();
void renderFrame();
void measureFPS();
void setupPerspective();
void updateAll();
 
int main(int argc, char** argv) {

  window.ShowMouseCursor(false);
  window.UseVerticalSync(true);
  window.SetFramerateLimit(60);
  clk.Reset();
  initOpenGL();
  loadAssets();
  finalInit();

  clk.Reset();
  while (window.IsOpened()) {
    updateAll();
    handleInput() ;     
    measureFPS();
    clk.Reset();
    renderFrame();
    window.Display();
  }
  return 0;
}


void updateAll(){
  //reset world control
  if(windowInFocus){
    window.SetCursorPosition(screenWidth>>1, screenHeight>>1);
  }

  camera.Update(clk.GetElapsedTime());
}


void initOpenGL() {
#ifdef FRAMEWORK_USE_GLEW
  GLint error = glewInit();
  if (GLEW_OK != error) {
    std::cerr << glewGetErrorString(error) << std::endl;
    exit(-1);
  }
  if (!GLEW_VERSION_2_0) {
    std::cerr << "This program requires OpenGL 2.0" << std::endl;
    exit(-1);
  }
#endif
  glViewport(0, 0, window.GetWidth(), window.GetHeight());
  glClearDepth(1.0f);
  glClearColor(0.15f, 0.15f, 0.15f, 1.0f); //grass green background
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_BLEND);
  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}




void loadAssets() {
  fpsstr.SetFont(sf::Font::GetDefaultFont ());
  fpsstr.SetText("0.0");
  fpsstr.SetSize(14);
  fpsstr.SetPosition(5, 5);

  test_obj = new RenderObject("resources/sphere.3ds");
  test_obj->translate(0.0f, 0.0f, -5.0f);


  noLightShader = new Shader("shaders/nolighting");
  if (!noLightShader->loaded()) {
    std::cerr << "noLight Shader failed to load" << std::endl;
    std::cerr << noLightShader->errors() << std::endl;
    exit(-1);
  }

}

void finalInit(){
  updateAll();
}

void finalize(){

}

void handleInput() {
  sf::Event evt;
  while (window.GetEvent(evt)) {
    switch (evt.Type) {
    case sf::Event::Closed: 
      window.Close(); 
      finalize();
      break;
    case sf::Event::Resized: 
      glViewport(0, 0, evt.Size.Width, evt.Size.Height);
      screenWidth = evt.Size.Width;
      screenHeight= evt.Size.Height;
      setupPerspective();
      break;
    case sf::Event::KeyPressed:
      {
	switch (evt.Key.Code){
	case sf::Key::Escape:
	case sf::Key::Q:
	  window.Close(); 
	  finalize();
	  break;
	case sf::Key::F1:
	  if(wireMode == GL_FILL)
	    wireMode = GL_LINE;
	  else
	    wireMode = GL_FILL;
	  break;
	case sf::Key::Add:
	  break;
	case sf::Key::Subtract:
	  break;
	default:
	  break;
	}
	break;
      }
    case sf::Event::KeyReleased:
      {
	switch (evt.Key.Code){
	default:
	  break;
	}
      }
      break;
    case sf::Event::MouseButtonPressed:
      if(!windowInFocus){
	windowInFocus = true;
	window.ShowMouseCursor(false);
      }
      break;
    case sf::Event::MouseButtonReleased:
      break;
    case sf::Event::MouseLeft:
      windowInFocus = false;
      window.ShowMouseCursor(true);
      break;
    default:
      break;
    
    }

    camera.HandleEvent(evt);
  }
}



void setupPerspective(){
  // Set up the projection matrix
  GLfloat aspectRatio = (GLfloat)window.GetWidth()/window.GetHeight();
  GLfloat nearClip = 0.1f;
  GLfloat farClip = 500.0f;
  GLfloat fieldOfView = 45.0f; 
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(fieldOfView, aspectRatio, nearClip, farClip);
}


void renderFrame() {
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glPolygonMode(GL_FRONT_AND_BACK,wireMode);
  setupPerspective();

  camera.Setup();
  
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glPushMatrix();{
    
    glUseProgram(noLightShader->programID());
    test_obj->Render(noLightShader);
    
    
  }
  glMatrixMode(GL_MODELVIEW);
  glPopMatrix();

  glUseProgram(0);
  window.Draw(fpsstr);
}




void measureFPS(){
  fps++;
  fpsAcc +=clk.GetElapsedTime();
  if(fpsAcc>0.5){
    char cstr[10];
    sprintf(cstr, "%.2f", float(fps)/fpsAcc);
    fpsstr.SetText(cstr);
    fpsAcc = 0.0;
    fps = 0;
  }
}
